/*
 * Copyright (c) 2020-2021 imlzw@vip.qq.com jweb.cc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cc.jweb.adai.web.system.generator.controller;

import cc.jweb.adai.web.system.generator.model.FieldModel;
import cc.jweb.adai.web.system.generator.model.TableModel;
import cc.jweb.adai.web.system.generator.model.TemplateModel;
import cc.jweb.adai.web.system.generator.service.CodeGenerator;
import cc.jweb.adai.web.system.generator.service.WebServerService;
import cc.jweb.adai.web.system.log.service.SysLogService;
import cc.jweb.adai.web.system.sys.model.SysLog;
import cc.jweb.adai.web.websocket.service.LogWebSocketService;
import cc.jweb.boot.common.exception.ParameterValidationException;
import cc.jweb.boot.common.lang.Result;
import cc.jweb.boot.controller.JwebController;
import cc.jweb.boot.db.Db;
import cc.jweb.boot.security.annotation.Logical;
import cc.jweb.boot.security.annotation.RequiresPermissions;
import cc.jweb.boot.utils.gson.GsonUtils;
import cc.jweb.boot.utils.lang.ResponseUtils;
import cc.jweb.boot.utils.lang.StringUtils;
import cc.jweb.boot.utils.lang.collection.MapUtils;
import cn.hutool.core.util.ZipUtil;
import com.jfinal.core.NotAction;
import com.jfinal.plugin.activerecord.Record;
import com.jfinal.render.FileRender;
import com.jfinal.render.Render;
import io.jboot.web.controller.annotation.RequestMapping;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletOutputStream;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@RequiresPermissions("system:generator:table:list")
@RequestMapping(value = "/generator/table", viewPath = "/WEB-INF/views/generator/table")
public class TableModelController extends JwebController {
    private static final Logger logger = LoggerFactory.getLogger(TableModelController.class);

    public void index() {
        render("index.html");
    }

    /**
     * 编辑页面
     */
    public void editPage() {
        String id = getPara("table_id");
        TableModel tableModel = TableModel.dao.findById(id);
        if (tableModel == null) {
            tableModel = new TableModel();
        }
        setAttr("detail", tableModel);
        render("edit.html");
    }

    /**
     * 编辑页面
     */
    public void fieldConfigPage() {
        FieldModel fieldModel = getColumnModel(FieldModel.class);
        setAttr("fieldModel", fieldModel);
        render("field_config.html");
    }

    /**
     * 加载分页列表数据
     */
    public void list() {
        Map<String, Object> params = getPageParamsPlus();
        Object[] values = getParaValues("values");
        Result result = new Result(false, "未知异常！");
        result.set("code", 400);
        if (StringUtils.isNotBlank(values)) { // formSelects的回显接口
            params.put("table_ids", values);
            result.setListData(Db.find(Db.getSqlPara("sys_table_model.queryPageList", params)));
            result.setSuccess(true);
            result.set("code", 0);
        } else {
            result = Db.paginate("sys_table_model.queryPageList", "sys_table_model.count", params);
            result.set("count", result.get(Result.LIST_TOTAL_KEY));
            result.setSuccess(true);
            result.set("code", 0);
        }
        renderJson(result);
    }

    /**
     * 加载分页列表数据
     */
    public void dbTableList() {
        Map<String, Object> params = getPageParamsPlus();
        Object[] values = getParaValues("values");
        Result result = new Result(false, "未知异常！");
        result.set("code", 400);
        if (StringUtils.isNotBlank(values)) { // formSelects的回显接口
            params.put("table_keys", values);
            List<Record> list = Db.find(Db.getSqlPara("sys_table_model.table_list", params));
            result.setListData(list);
            result.setSuccess(true);
            result.set("code", 0);
        } else {
            result = Db.paginate("sys_table_model.table_list", "sys_table_model.table_count", params);
            result.set("count", result.get(Result.LIST_TOTAL_KEY));
            result.setSuccess(true);
            result.set("code", 0);
        }
        List<Record> records = (List<Record>) result.get(Result.DATA_KEY);
        for (Record item : records) {
            Object tableName = item.get("TABLE_NAME");
            Object tableComment = item.get("TABLE_COMMENT");
            if (tableComment != null && tableComment.toString().trim().length() > 0) {
                item.set("TABLE_TITLE", tableName + "(" + tableComment + ")");
            } else {
                item.set("TABLE_TITLE", tableName);
            }
        }
        renderJson(result);
    }


    /**
     * 加载分页列表数据
     */
    public void dbFieldList() {
        Map<String, Object> params = getPageParamsPlus();
        params.put("limit", 999);
        Result result = new Result(false, "未知异常！");
        result.set("code", 400);
        Object[] values = getParaValues("values");
        if (StringUtils.isNotBlank(values)) { // formSelects的回显接口
            params.put("field_keys", values);
            List<Record> list = Db.find(Db.getSqlPara("sys_table_model.field_list", params));
            result.setListData(list);
            result.setSuccess(true);
            result.set("code", 0);
        } else {
            result = Db.paginate("sys_table_model.field_list", "sys_table_model.field_list_count", params);
            result.set("count", result.get(Result.LIST_TOTAL_KEY));
            result.setSuccess(true);
            result.set("code", 0);
        }
        renderJson(result);
    }

    /**
     * 保存用户信息（新增与修改）
     */
    @RequiresPermissions(value = {"system:generator:table:add", "system:generator:table:edit"}, logical = Logical.OR)
    public void save() {
        TableModel tableModel1 = getModel(TableModel.class);
        TableModel tableModel = getColumnModel(TableModel.class);
        TableModel oldModel = null;
        Object id = tableModel.get("table_id");
        if (id != null) {
            oldModel = TableModel.dao.findById(id);
        }
        if (oldModel != null) { // 编辑
            tableModel.update();
        } else { // 新增
            tableModel.set("create_datetime", new Date());
            tableModel.save();
        }
        // 采集字段列表记录
        Map<String, String[]> paraMap = getParaMap();
        List<Record> existIds = Db.find("select field_id from sys_field_model where table_id = ? ", tableModel.getTableId());
        List<Object> deleteIds = new ArrayList<>(existIds.size());
        List<Record> records = new ArrayList<>();
        for (String key : paraMap.keySet()) {
            if (key.startsWith("field_id_")) {
                String fieldId = key.substring(9);
                Record record = null;
                if (!fieldId.startsWith("-")) {
                    FieldModel fieldModel = (FieldModel) FieldModel.dao.findById(fieldId);
                    if (fieldModel != null && fieldModel.get("field_id") != null) {
                        record = fieldModel.toRecord();
                    }
                }
                if (record == null) {
                    record = new Record();
                    record.set("table_id", tableModel.getTableId());
                    record.set("create_datetime", new Date());
                }
                record.set("field_name", getPara("field_name_" + fieldId));
                record.set("field_key", getPara("field_key_" + fieldId));
                record.set("field_type", getPara("field_type_" + fieldId));
                record.set("field_length", getPara("field_length_" + fieldId));
                record.set("decimal_point", getPara("decimal_point_" + fieldId));
                record.set("order_no", getPara("order_no_" + fieldId));
                record.set("is_primary", "on".equals(getPara("is_primary_" + fieldId)) ? 1 : 0);
                record.set("is_nullable", "on".equals(getPara("is_nullable_" + fieldId)) ? 1 : 0);
                record.set("field_config", getPara("field_config_" + fieldId));
                record.set("field_order", getPara("field_order_" + fieldId));
                record.set("field_default", getPara("field_default_" + fieldId, "NULL"));
                records.add(record);
            }
        }
        // 查找出需要删除的列表
        for (Record existRecord : existIds) {
            boolean isExist = false;
            for (Record updateRecord : records) {
                if (existRecord.get("field_id").equals(updateRecord.get("field_id"))) {
                    isExist = true;
                    break;
                }
            }
            if (!isExist) {
                deleteIds.add(existRecord.get("field_id"));
            }
        }

        // 保存记录
        for (Record record : records) {
            if (record.get("field_id") != null) {
                Db.update("sys_field_model", "field_id", record);
            } else {
                Db.save("sys_field_model", "field_id", record);
            }
        }

        // 删除记录
        for (Object deleteId : deleteIds) {
            FieldModel.dao.deleteById(deleteId);
        }

        SysLogService.service.setSyslog(SysLog.STATUS_SUCCESS, " 保存表模型【" + (tableModel.getTableName()) + "】成功！");
        renderJson(new Result(true, "保存表模型信息成功！"));
    }

    /**
     * 删除记录
     */
    @RequiresPermissions("system:generator:table:del")
    public void delete() {
        String[] ids = getParaValues("ids");
        if (ids == null || ids.length <= 0) {
            renderJson(new Result(true, "删除成功！"));
            return;
        }
        boolean b = true;
        for (String id : ids) {
            b = b & TableModel.dao.deleteByIds(id);
        }
        String paramSql = "";
        for (String id : ids) {
            paramSql += ",?";
        }
        int delete = Db.delete("delete from sys_field_model where table_id in (" + paramSql.substring(1) + ")", ids);
        SysLogService.service.setSyslog(b ? SysLog.STATUS_SUCCESS : SysLog.STATUS_FAILURE, "删除表模型【id:" + StringUtils.join(ids, ",") + "】" + (b ? "成功" : "失败") + " !");
        renderJson(new Result(b, b ? "删除成功！" : "删除失败！"));
    }

    public void generateCode() {
        Result result = new Result(false);
        TableModel tableModel = TableModel.dao.findById(getPara("table_id"));
        List<FieldModel> fieldModels = FieldModel.dao.find("select * from sys_field_model where table_id = ? order by order_no asc", getPara("table_id"));
        try {
            CodeGenerator.generatorModel(tableModel, fieldModels);
            result.setSuccess(true);
        } catch (IOException e) {
            logger.error("生成代码异常！", e);
            LogWebSocketService.sendError(e);
        } catch (Exception e) {
            logger.error("生成代码异常！", e);
            LogWebSocketService.sendError(e);
        }
        renderJson(result);
    }


    /**
     * 预览生成代码
     */
    public void preview() {
        String tableId = getPara("table_id");
        TableModel tableModel = TableModel.dao.findById(tableId);
//        List fileList = readFileList(new File(CodeGenerator.getCodeOutputPath(tableModel)), "");
//        setAttr("fileList", fileList);
        setAttr("tableModel", tableModel);
        render("preview2.html");
    }

    /**
     * XCE对象的api接口
     *
     * @throws IOException
     */
    public void xceApi() throws IOException {
        if (isParaBlank("table_id")) {
            renderJson(new Result(false, "表模型编号不存在！"));
            return;
        }
        String oper = getPara("oper");
        if (isParaBlank("oper")) {
            renderJson(new Result(false, "未知操作！"));
            return;
        }
        TableModel tableModel = TableModel.dao.findById(getPara("table_id"));
        if (tableModel == null) {
            renderJson(new Result(false, "表模型不存在！"));
            return;
        }
        switch (oper) {
            case "list":
                listFile(tableModel);
                break;
            case "content":
                download(tableModel);
                break;

        }
    }

    /**
     * 文件列表
     */
    @NotAction
    public void listFile(TableModel tableModel) {
        List fileList = readFileList(new File(CodeGenerator.getModelTemplateOutputFilePath(tableModel)), "");
//        Result result = new Result(true);
//        result.setData(fileList);
        renderJson("{ \"success\": true, \"data\": "+ (GsonUtils.get().toJson(fileList)) +"}");
    }

    /**
     * 递归加载文件列表
     *
     * @param codePath
     * @return
     */
    @NotAction
    private List readFileList(File codePath, String parentPath) {
        List list = new ArrayList();
        File[] files = codePath.listFiles();
        if (files != null) {
            for (File file : files) {
                String filePath = parentPath + "/" + file.getName();
                Map<String, Object> fileInfo = MapUtils.of("title", file.getName(),
                        "spread", file.isDirectory(),
                        "path", filePath,
                        "id", filePath
                );
                fileInfo.put("isFolder", file.isDirectory());
                if (file.isDirectory()) {
                    fileInfo.put("children", readFileList(file, filePath));
                }
                list.add(fileInfo);
            }
        }
        return list;
    }

    @NotAction
    private String fixPath(String path) {
        if (StringUtils.isBlank(path)) {
            return path;
        }
        if (path.indexOf("~") >= 0 || path.indexOf("..") >= 0) {
            throw new ParameterValidationException("路径不正确！");
        }
        path = path.replace("..", "").replace("//", "/");
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        return path;
    }

    /**
     * 文件下载接口
     *
     * @throws IOException
     */
    @NotAction
    public void download(TableModel tableModel) throws IOException {
        String path = fixPath(getPara("path"));
        File downFile = new File(CodeGenerator.getModelTemplateOutputFilePath(tableModel) + path);
        if (!downFile.exists()) {
            renderJson(new Result(false, "找不到文件！"));
            return;
        }
        render(new Render() {
            private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream;";

            @Override
            public void render() {
                response.setHeader("Accept-Ranges", "bytes");
                //String newReportName  = reportName == null?"安全分析统计报告":reportName;
                String downloadFileNameHeader = ResponseUtils.getDownloadFileNameHeader(getRequest(), downFile.getName());
                response.setHeader("Content-disposition", downloadFileNameHeader);
                ServletOutputStream outputStream = null;
                InputStream fis = null;
                try {
                    outputStream = response.getOutputStream();
                    fis = new BufferedInputStream(new FileInputStream(downFile));
                    byte[] cache = new byte[1024];
                    int count = 0;
                    while ((count = fis.read(cache)) != -1) {
                        outputStream.write(cache, 0, count);//将缓冲区的数据输出到浏览器
                    }
                    outputStream.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (outputStream != null) {
                        try {
                            outputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (fis != null) {
                        try {
                            fis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        });
    }

    /**
     * 预览文件
     */
    public void previewFile() {
        String tid = getPara("tid");
        String path = getPara("path");
        String charset = getPara("charset");
        Result result = new Result(false, "未知错误！");
        if (path == null || tid == null || path.trim().length() <= 0 || tid.trim().length() <= 0) {
            result.setMessage("无效的预览参数！");
            renderJson(result);
            return;
        }
        if (path != null && path.matches("\\.{2,}") || (tid != null && tid.matches("\\.{2,}"))) {
            result.setMessage("无效的文件路径！");
            renderJson(result);
            return;
        }
        TableModel tableModel = TableModel.dao.findById(tid);
        String previewFile = CodeGenerator.getCodeOutputPath(tableModel) + path;
        String fileContent = null;
        try {
            File file = new File(previewFile);
            fileContent = FileUtils.readFileToString(file, StringUtils.isBlank(charset) ? "UTF-8" : charset);
            result.setSuccess(true);
            result.setMessage("读取成功！");
            result.setData(fileContent);
        } catch (IOException e) {
            result.setSuccess(false);
            result.setMessage("文件读取IO异常！");
            logger.error("文件预览异常！", e);
        }
        renderJson(result);
    }

    public void download() {
        String tableId = getPara("table_id");
        TableModel tableModel = TableModel.dao.findById(tableId);
        File zip = ZipUtil.zip(CodeGenerator.getCodeOutputPath(tableModel));
        render(new FileRender(zip, tableModel.getTableName() + ".zip"));
    }

    /**
     * 代码生成页面
     */
    public void codePage() {
        TableModel tableModel = TableModel.dao.findById(getPara("table_id"));
        String port = getPara("port");
        setAttr("hasStartedWeb", WebServerService.hasStartedWeb(Integer.parseInt(port)));
        setAttr("tableModel", tableModel);
        render("code.html");
    }

    /**
     * 代码编译
     */
    public void compile() {
        String tableId = getPara("table_id");
        TableModel tableModel = TableModel.dao.findById(tableId);
        CodeGenerator.compileCode(tableModel);
        renderJson(new Result(true, "编译完成！"));
        return;
    }


    public void startWeb() {
        String tableId = getPara("table_id");
        TableModel tableModel = TableModel.dao.findById(tableId);
        try {
            int webPort = CodeGenerator.startTestWeb(tableModel);
            renderJson(new Result(true, "正在启动Web[:" + webPort + "]服务！").set("port", webPort));
        } catch (Exception exception) {
            logger.error("启动Web服务异常！", exception);
            renderJson(new Result(false, "启动Web服务异常！" + exception.getMessage()));
        }
    }

    public void stopWeb() {
        String port = getPara("port");
        try {
            CodeGenerator.stopTestWeb(Integer.parseInt(port));
            renderJson(new Result(true, "停止Web[:" + port + "]服务成功！"));
        } catch (Exception exception) {
            logger.error("停止Web[:" + port + "]服务异常！", exception);
            renderJson(new Result(false, "停止Web[:" + port + "]服务异常！" + exception.getMessage()));
        }
    }

    /**
     * 部署代码
     */
    @RequiresPermissions("system:generator:table:deploy")
    public void deploy() {
        String table_id = getPara("table_id");
        TableModel tableModel = TableModel.dao.findById(table_id);
        try {
            CodeGenerator.deployCode(tableModel);
            renderJson(new Result(true, "部署代码成功！"));
        } catch (Exception exception) {
            logger.error("部署代码异常！", exception);
            renderJson(new Result(false, "部署代码异常," + exception.getMessage()));
        }
    }

}